'''
# 先写个O(n^2)的算法凑数
n = int(input())
a = [*map(int, input().split())]
dp = [1 for i in range(n)]
for i in range(n):
    for j in range(i):
        # a[i]是下一个木桩，a[j]是当前木桩
        #下一个木桩不大于当前木桩的意思是，当前木桩大于或等于下一个木桩
        if a[j] >= a[i]:
            dp[i] = max(dp[i], dp[j] + 1)
print(max(dp))
'''
# 等学会了O(nlogn)的算法再接着写。这题应该是用例出小了，O(n^2)也随便过呢？
import bisect # 在排序好的数组中二分查找的模块
n = int(input())
a = [*map(int, input().split())]
dp, l = [a[0]], 1 # dp初始存a[0]，初始长度为1
for i in a[1:]: # 从a[1]开始遍历
    if i <= dp[-1]: # 如果新的高度不大于前面的木桩中连续长度最长的最后一根桩子。
        dp.append(i) # 就把连续长度最长值+1，此长度的最后一根柱子就是当前这根柱子。
        l += 1 # 并把最长长度和索引+1
    else: # 否则，看看这跟有没有比之前长度的长的，替换掉。
        # 首先，连续长度越长，肯定木桩越来越矮的
        # 比如是5 3 3我们要插入4，就要把中间那个3替换掉
        # 而因为这个算法只能二分查找从小到大的列表，所以我们把列表逆序再查找
        # 这里本来有段查找方法的分析，但是我分析错了，捂脸，就删了！
        # bisect和bisect_left的具体用法可以自己百度一下，我也是才知道的，大不了两个都试一试。。
        j = bisect.bisect_left(dp[::-1], i)
        # print(-j)
        dp[-j] = i
    # print(dp)
print(l)
# 费劲写了大概1个小时，把用时从300多ms强化成了20多ms
# 行吧，建议出个二分查找从大到小的！！
'''
我顺便测了下逆序的用时
from datetime import datetime as dt
a = [i for i in range(1000)]
st = dt.now()
for i in range(int(1e5)):
    b = a.copy()
print((dt.now()-st).total_seconds())
0.37799s
st = dt.now()
for i in range(int(1e5)):
    b = a[::-1].copy()
print((dt.now()-st).total_seconds())
0.738012s
st = dt.now()
for i in range(int(1e5)):
    b = [i for i in range(1000)]
print((dt.now()-st).total_seconds())
4.601731s
st = dt.now()
for i in range(int(1e5)):
    b = [0 for i in range(1000)]
print((dt.now()-st).total_seconds())
3.85868s
st = dt.now()
for i in range(int(1e5)):
    b = [0] * 1000
print((dt.now()-st).total_seconds())
0.398929s
震惊，原来列表推导式很慢的。。
'''
# -------------------------------------------------------------------------------

# 比赛环境为python3.8.6版本自带的IDLE，最好早点熟悉一下。。这个东西的提示时有时无

# 菜单栏最右边的Help的Python Docs 比赛时候也可以看，不过建议还是提前多了解了解，

# 比赛的时候至少知道在文档找什么能用的上。

# IDLE是下载Python时自带的，在按住win+s搜索IDLE就能搜到了。

# 然后点左上角的File可以创建新文件，然后按F5可以运行。
